my %tool = (
    'apt' => \&_apt,
    'yum' => \&_yum,
);

my $type = fetch_val("package_type");
$type ||= "apt" if in_path("apt-get");
$type ||= "yum" if in_path("yum");

die "Unknown package management system\n"
    unless $type;

my $pkg;
$pkg ||= "rpm" if in_path("rpm");
$pkg ||= "dpkg" if in_path("dpkg");

die "Unknown packaging format\n"
    unless $pkg;

if (my $repo = fetchall_hash('package_repo'))
{
    if ($type eq 'yum')
    {
	my $conf = '';

	while (my ($name, $info) = each %{ $repo })
	{
	    $info = { baseurl => $info } unless ref $info;

	    $conf .= "[$name]\n";
	    $conf .= "name=$name\n";
	    $conf .= "$_=$info->{$_}\n" foreach keys %{ $info };
	    $conf .= "\n";
	}

	text_install('/etc/yum.repos.d/rollout.repo', $conf, -rcs_missing_ok => 1);

	file_comment($_, qr/./, -rcs_missing_ok => 1)
	    foreach grep !m|/rollout\.repo$|, </etc/yum.repos.d/*.repo>;
    }
    elsif ($type eq 'apt')
    {
	my $conf = '';

	while (my ($baseurl, $info) = each %{ $repo })
	{
	    $info = { type=>'deb', modules=>[ $info ] } unless ref $info;

	    $conf .= "$info->{type} $baseurl @{$info->{modules}}\n";
	}

	text_install('/etc/apt/sources.list.d/rollout.list', $conf, -rcs_missing_ok => 1);

	file_comment($_, qr/./, -rcs_missing_ok => 1)
	    foreach grep !m|/rollout\.list$|, </etc/apt/sources.list.d/*.list>;
    }
}

sub update_packages
{
    return $tool{$type}("update");
}

CACHE: {
my %installed;

sub package_installed
{
    my ($name) = @_;

    return -e "/var/lib/dpkg/info/$name.list"
	if $pkg eq 'dpkg';

    if ($pkg eq 'rpm')
    {
	unless ($installed{_loaded})
	{
	    open my $fd, "-|", "rpm", "-qa", "--queryformat=%{NAME}\t%{VERSION}\t%{RELEASE}\n";
	    while (defined(my $line = <$fd>))
	    {
		chomp;
		my ($name, $version, $release) = split(/\t/, $line);
		$installed{$name} = $version;
	    }
	    close $fd;

	    $installed{_loaded} = time;
	}

	return $installed{$name} || 0;
    }
}
}

sub package_install
{
    return $tool{$type}("install", @_);
}

sub package_remove
{
    return $tool{$type}("remove", @_);
}

sub _apt
{
    my ($op, @packages) = @_;
    my $args = pop @packages if ref $packages[-1] eq 'HASH';
    $args ||= {};

    my @args = ("-qq");

    push @args, map +("-o" => "RPM::Install-Options::=--$_"), grep delete $args->{$_}, qw(
	excludedocs force ignoresize ignorearch ignoreos nodeps
	noscripts nopre nopost nopreun nopostun
	notriggers notriggerin notriggerun notriggerpostun repackage
    ) if in_path("rpm");

    push @args, +("-o" => "$_::=$args->{$_}"), sort keys %{ $args };

    command("apt-get", @args, "update")
	unless $Rollout::apt_updated++;

    return command("apt-get", @args, "upgrade", "--show-upgraded", "--assume-yes")
	if $op eq 'update';

    return if $op eq 'remove' and !grep(package_installed($_), @packages);

    my $end = length $Rollout::log_messages;
    command("apt-get", @args, $op, "--show-upgraded", "--assume-yes", @packages) == 0
	or die "Couldn't apt-get install @packages!\n";
    return substr($Rollout::log_messages, $end) =~ /^(Preparing|Unpacking|Setting up|Removing)/mg;
}

sub _yum
{
    my ($op, @packages) = @_;
    my $args = pop @packages if ref $packages[-1] eq 'HASH';
    $args ||= {};

    my $arch = delete $args->{arch};
    my $flags = { ignore => qr/^Package \S+ - \S+ already installed and latest version/ };

    @packages =
	map { $_ .= ".$arch" if $arch; $_ } # optional .x86_64 suffix
	map { s/=/-/; $_ } # name=ver => name-ver
	grep { !/^\@/ or $op =~ /^group/ or _yum("group$op", substr $_, 1) && 0 }
	grep defined && length,
	@packages;

    my @args = ("-d", "1", "-y");

    push @args, "--tsflags", join(",", keys %{ $args })
	if keys %{ $args };

    return command("yum", @args, "update")
	if $op eq 'update';

    return if $op eq 'remove' and !grep(package_installed($_), @packages);

    my $end = length $Rollout::log_messages;
    command("yum", @args, $op, @packages, $flags) == 0
	or die "Couldn't yum install @packages!\n";
    return substr($Rollout::log_messages, $end) =~ /^(Installed:|Updated:)/mg;
}

1;
